/* 
 * Copyright (c) 2012, Fromentin Xavier, Schnell Michaël, Dervin Cyrielle, Brabant Quentin
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *      * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *      * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *      * The names of its contributors may not be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL Fromentin Xavier, Schnell Michaël, Dervin Cyrielle OR Brabant Quentin 
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
package kameleon.document;

import java.io.Serializable;
import java.util.Hashtable;

import kameleon.exception.InvalidPropertyException;
import kameleon.exception.KameleonException;

/**
 * Manages the properties for document elements.
 * 
 * <p>Every element of the document (the document included) has specific properties.
 * Every property is handled by a name ({@code key}) and its value ({@code value}).
 * 
 * <p>The interface {@code ElementPropertiesDefaultNames} provides the conventional
 * names for the most common properties. To ensure a proper handling, these names 
 * should be used.
 * 
 * @author		Fromentin Xavier, Schnell Michaël, Dervin Cyrielle, Brabant Quentin
 * @version		1.0
 * 
 * @see			ElementPropertiesDefaultNames
 */
public class ElementProperties implements Serializable, ElementPropertiesDefaultNames {

	/**
	 * Needed to serialize this class.
	 * 
	 * @see		java.io.Serializable
	 */
	private static final long serialVersionUID = -5175984394413753535L;
	
	/**
	 * Format used to display a property and its value.
	 */
	private static final String PROPERTY_DISPLAY = "\tProperty '%s' - '%s'\n" ; //$NON-NLS-1$

	/**
	 * Hash table containing all the saved properties.
	 */
	private Hashtable<String, Object> properties ;

	/**
	 * Builds an instance with no properties.
	 */
	public ElementProperties() {
		super() ;
		this.properties = new Hashtable<String, Object>() ;
	}// ElementProperties()

	/**
	 * Adds a new property.
	 * 
	 * <p>If there is already a property with the given name, the value is
	 * replaced.
	 * 
	 * @param 	key
	 * 			property name
	 * 
	 * @param 	value
	 * 			property value
	 * 
	 * @throws	InvalidPropertyException
	 * 			if either key or value are {@code null}
	 */
	public void setProperty(String key, Object value) throws InvalidPropertyException {
		try {
			this.properties.put(key, value) ;
		} catch (NullPointerException npe) {
			throw new InvalidPropertyException(key) ;
		}// try
	}// setProperty(String, Object)

	/**
	 * Adds a new property and sets it's value to {@code true}.
	 * 
	 * <p>If there is already a property with the given name, the value is
	 * replaced.
	 * 
	 * @param 	key
	 * 			property name
	 * 
	 * @throws	InvalidPropertyException
	 * 			if key is {@code null}
	 * 
	 * @see		#setProperty(String, Object)
	 */
	public void setProperty(String key) throws InvalidPropertyException {
		this.setProperty(key, Boolean.TRUE) ;
	}// setProperty(String)

	/**
	 * Returns the value of the requested property.
	 * 
	 * <p>Returns {@code null} if the property doesn't exist.
	 * 
	 * @param 	key
	 * 			key of the property whose value is requested
	 * 
	 * @return	Value of the requested property or {@code null} if the 
	 * 			property doesn't exist
	 * 
	 * @throws 	InvalidPropertyException
	 * 			if {@code key} is {@code null}
	 */
	public Object getProperty(String key) throws InvalidPropertyException {
		if (key == null) {
			throw new InvalidPropertyException(key) ;
		}// if
		return this.properties.get(key) ;
	}// getProperty(String)

	/**
	 * Indicates if the given property has been set for this instance.
	 * 
	 * @param 	key
	 * 			searched property
	 * 
	 * @return	{@code true} if the property is known by this instance,
	 * 			{@code false} otherwise
	 */
	public boolean isProperty(String key) {
		try {
			return (key != null) && (this.getProperty(key) != null) ;
		} catch (InvalidPropertyException e) {
			/* This should not happen. */
		}// try
		return false ;
	}// isProperty(String)

	/**
	 * Copies all the properties from the given {@code ElementProperties} 
	 * to this instance.
	 * 
	 * @param 	ep
	 * 			container for the properties which should be applied to the receiver
	 */
	public void setProperties(ElementProperties ep) {
		try {
			for(String key : ep.properties.keySet()) {
				this.setProperty(key, ep.getProperty(key)) ;
			}// for
		} catch(KameleonException ke) {
			/* This should not happen. */
		}// try
	}// setProperties(ElementProperties)
	
	/**
	 * Returns the properties stored by this instance as a {@code String}.
	 * 
	 * <p>The values for the given keys are displayed using their respective
	 * {@code toString} function.
	 * 
	 * @return	Properties stored by this instance as a {@code String}
	 */
	@Override
	public String toString() {
		StringBuilder s = new StringBuilder() ;
		for(String key : this.properties.keySet()) {
			try {
				s.append(String.format(PROPERTY_DISPLAY, key, this.getProperty(key))) ;
			} catch (InvalidPropertyException e) {
				/* This should not happen. */
			}// try
		}// for
		return s.toString() ;
	}// toString()
	
}// class ElementProperties